﻿#region Header
// Copyright 2007-2011, Jerónimo Milea
// 
// This file is part of NetJsWire.
// 
//     NetJsWire is free software: you can redistribute it and/or modify
//     it under the terms of the GNU General Public License as published by
//     the Free Software Foundation, either version 3 of the License, or
//     (at your option) any later version.
// 
//     NetJsWire is distributed in the hope that it will be useful,
//     but WITHOUT ANY WARRANTY; without even the implied warranty of
//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//     GNU General Public License for more details.
// 
//     You should have received a copy of the GNU General Public License
//     along with NetJsWire.  If not, see <http://www.gnu.org/licenses/>.
#endregion

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Windows.Forms;
using System.Xml;
using NetJsWire.JsonRpcHandler;
using NetJsWire.JsonRpcHandler.Util;
using NetJsWire.Util;
using NetJsWire.Widgets.Config;
using NetJsWire.Widgets.Data;
using NetJsWire.Widgets.Ui.Form;
using NetJsWire.Widgets.Ui.Layout;
using NetJsWire.Widgets.Ui.ToolBar;
using NetJsWire.Widgets.Ui.Window;
using Newtonsoft.Json;
using Button = NetJsWire.Widgets.Ui.ToolBar.Button;
using ComboBox = NetJsWire.Widgets.Ui.Form.ComboBox;
using MenuItem = NetJsWire.Widgets.Ui.ToolBar.MenuItem;
using Panel = NetJsWire.Widgets.Ui.Layout.Panel;
using TextField = NetJsWire.Widgets.Ui.ToolBar.TextField;

namespace NetJsWire.Widgets.Core
{
    ///<summary>
    ///  Base client class can be a graphics widget or a helper component.
    ///</summary>
    public abstract class Element : IRenderer
    {
        public static int AvailableHashCode;
        protected static string[] ReservedWords;
        protected readonly Dictionary<string, object> ConstructionProperties = new Dictionary<string, object>();
        private readonly Dictionary<string, object> _equalProperties;
        private readonly int? _hashCode;
        private readonly Dictionary<string, object> _properties;
        protected Dictionary<string, string> ClientEvents = new Dictionary<string, string>();
        protected Queue<Command> Commands = new Queue<Command>();
        protected Queue<Command> DirectCommands = new Queue<Command>();
        protected Dictionary<string, string> KeyNavs = new Dictionary<string, string>();
        protected Queue<Command> PreCodeCommands = new Queue<Command>();
        private dynamic _base;
        private string _classname;
        private string _clientID;
        private string _clientParentToRender;
        private XmlNode _controlNode;
        private string _internalID;

        // static constructor gets called only once for the lifetime of the app
        static Element()
        {
            // This is the list of ConfigOptions that should never be rendered as quoted strings.
            ReservedWords = new[] { 
                                "handler", 
                                "plugins", 
                                "sortInfo", 
                                "groupTextTpl", 
                                "cm", 
                                "ds", 
                                "autoCreate", 
                                "items", 
                                "renderTo", 
                                "parent", 
                                "toolbar", 
                                "innerGrid", 
                                "resizeEl", 
                                "autoCreate", 
                                "desktop", 
                                "width", 
                                "height", 
                                "tpl", 
                                "colTpl", 
                                "fields", 
                                "tbar", 
                                "bbar", 
                                "tools", 
                                "defaults", 
                                "baseParams", 
                                "layoutConfig", 
                                "autoEl",
                                "validator",
                                "menu",
                                "directFn",
                                "reader",
                                "sortInfo",
                                "listeners",
                                "proxy",
                                "api",
                                "margins",
                                "store",
                                "columns",
                                "columnSort",
                                "columnResize"
            };
        }

        /// <summary>
        ///   Avoid parameterless constructor.
        /// </summary>
        private Element()
        {
        }

        /// <summary>
        ///   Default constructor.
        /// </summary>
        /// <param name = "baseObject">The design object this renderer manages.</param>
        public Element( object baseObject )
        {
            _hashCode = AvailableHashCode++;
            _properties = new Dictionary<string, object>();
            _equalProperties = new Dictionary<string, object>();
            Plugins = new List<object>();

            _base = baseObject;

            lock ( WebApplication.Instance.Objects )
            {
                if ( !WebApplication.Instance.Objects.ContainsKey( InternalID ) )
                {
                    WebApplication.Instance.Objects.Add( InternalID, Base );
                    WebApplication.Instance.HasChanges = true;
                }
            }
        }

        /// <summary>
        ///   The client class name.
        /// </summary>
        [Browsable( false )]
        public string ClassName
        {
            get
            {
                if ( string.IsNullOrEmpty( _classname ) )
                {
                    var cName = Helper.GetClassName( this );
                    if ( !string.IsNullOrEmpty( cName ) )
                    {
                        _classname = cName;
                    }
                    else
                    {
                        if ( GetType().FullName.StartsWith( "Ext.NET", StringComparison.InvariantCultureIgnoreCase ) )
                        {
                            var ret = GetType().FullName.Replace( "Ext.NET.", "" );
                            _classname = ret.Replace( "Renderer.", "Ext." );
                        }
                        else
                        {
                            var ret = GetType().BaseType.FullName.Replace( "Ext.NET.", "" );
                            _classname = ret.Replace( "Renderer.", "Ext." );
                        }
                    }
                }
                return _classname;
            }
            set { _classname = value; }
        }

        internal bool IsInContainer
        {
            get { return BaseParent is IWidgetContainer; }
        }

        [Browsable( false )]
        public XmlNode StylesNode
        {
            get { return ControlNode[ "styles" ]; }
        }

        public virtual Element ParentRenderer
        {
            get
            {
                try
                {
                    if ( Base is Control )
                    {
                        return Base.Parent.Renderer;
                    }

                    if ( Base is Component && Base is Store )
                    {
                        return Base.ContainerControl.Renderer;
                    }

                    if ( Base is Component && Base is ToolStripItem )
                    {
                        if ( Base.OwnerItem != null )
                        {
                            return Base.OwnerItem.Renderer;
                        }

                        return Base.Owner.Renderer;
                    }

                    return null;
                    /*
                    PropertyInfo pi = Base.GetType().GetProperty( "Parent" );
                    if ( pi == null )
                        pi = Base.GetType().GetProperty( "OwnerItem" );
                    if ( pi != null )
                    {
                        object o = pi.GetValue( Base, null );
                        if ( o != null )
                        {
                            object val = o.GetType().GetProperty( "Renderer" ).GetValue( o, null );
                            if ( val != null )
                                return ( Element ) val;
                            else
                                return null;
                        }
                        else
                        {
                            pi = Base.GetType().GetProperty( "Owner" );
                            o = pi.GetValue( Base, null );
                            if ( o != null )
                            {
                                object val = o.GetType().GetProperty( "Renderer", typeof( IRenderer ) ).GetValue( o, null );
                                if ( val != null )
                                    return ( Element ) val;
                                else
                                    return null;
                            }
                            return null;
                        }
                    }
                    else
                    {
                        return null;
                    }
                     * */
                }
                catch
                {
                    return null;
                }
            }
        }

        public Dictionary<string, object> Properties
        {
            get { return _properties; }
        }

        public string ConstructorParameters { get; set; }

        public Dictionary<string, object> EqualProperties
        {
            get { return _equalProperties; }
        }

        #region IRenderer Members

        public event EventHandler AfterRender;

        ///<summary>
        ///  The internal ID of the element that identifies it in the application objects collection.
        ///  <remarks>
        ///    When this property is set manually the ClientId is set to the same value
        ///  </remarks>
        ///</summary>
        ///<exception cref = "NullReferenceException">Thrown if value is set to null.</exception>
        [Browsable( false )]
        public string InternalID
        {
            get
            {
                if ( string.IsNullOrEmpty( _internalID ) )
                {
                    var hash = GetHashCode();
                    if ( hash < 0 )
                        hash = -hash;
                    _internalID = ClassName.Substring( ClassName.LastIndexOf( '.' ) + 1 ) + "_" + hash;
                }
                return _internalID;
            }
            set
            {
                if ( value == null )
                {
                    throw new NullReferenceException( "InternalID cannot be set to null value." );
                }
                var needToAdd = false;

                if ( WebApplication.Instance.Objects.ContainsKey( _internalID ) )
                {
                    WebApplication.Instance.Objects.Remove( _internalID );
                    needToAdd = true;
                }
                _internalID = value;
                if ( needToAdd )
                {
                    WebApplication.Instance.Objects.Add( _internalID, _base );
                    _clientID = value;
                }
            }
        }

        ///<summary>
        ///  The client ID the object is going to have in Javascript.
        ///  This ID can be later referenced in Javascript code, the naming convention is pretty easy to follow, it goes like this:
        ///  NetJsWire.App.TopLevelContainer.AnyOtherContainer.YourObject, i.e.: if you have a Window named 'MyWindow' and the Window has a TextField named 'textField1'
        ///  then the ClientID will become: NetJsWire.App.MyWindow.textField1
        ///</summary>
        public string ClientID
        {
            get
            {
                if ( string.IsNullOrWhiteSpace( _clientID ) )
                {
                    var tmp = string.Empty;
                    GetClientIDWithNamespace( ref tmp, Base );
                    if ( !string.IsNullOrEmpty( tmp ) )
                        _clientID = "NetJsWire.App." + tmp + "." + BaseName;
                    else
                        _clientID = "NetJsWire.App." + BaseName;
                }
                return _clientID;
            }
            set { _clientID = value; }
        }

        /// <summary>
        ///   The Base object this Element represents, can be a Control or Component.
        /// </summary>
        [Browsable( false )]
        public dynamic Base
        {
            get { return _base; }
            set { _base = value; }
        }

        /// <summary>
        ///   The name of the <see cref = "Base" /> object.
        /// </summary>
        [Browsable( false )]
        public string BaseName
        {
            get
            {
                string name;
                try
                {
                    name = Base.Name;
                }
                catch
                {
                    name = _internalID;
                }

                return name;
            }
        }


        /// <summary>
        ///   Is the Element rendered?
        /// </summary>
        [Browsable( false )]
        public bool Rendered { get; set; }

        /// <summary>
        ///   Renders the element to generate the client code.
        /// </summary>
        public virtual void Render()
        {
        }

        /// <summary>
        ///   Sets a construction property for the client object.
        /// </summary>
        /// <param name = "name">Name of the property.</param>
        /// <param name = "value">Value of to initialize the property.</param>
        public virtual void SetConstructionProperty( string name, object value )
        {
            if ( !Rendered )
            {
                if ( ConstructionProperties.ContainsKey( name ) )
                {
                    ConstructionProperties[ name ] = value;
                }
                else
                {
                    ConstructionProperties.Add( name, value );
                }
            }
            else
            {
                SetAttribute( ControlNode[ "options" ], name, value );
            }
        }

        /// <summary>
        ///   Removes a construction property from the list.
        /// </summary>
        /// <param name = "name">Name of the property.</param>
        public virtual void RemoveConstructionProperty( string name )
        {
            if ( ConstructionProperties.ContainsKey( name ) )
            {
                ConstructionProperties.Remove( name );
            }
            else
            {
                if ( ControlNode[ "options" ] != null && ControlNode[ "options" ].Attributes[ name ] != null )
                {
                    ControlNode[ "options" ].Attributes.RemoveNamedItem( name );
                }
            }
        }

        ///<summary>
        ///  Calls a client command.
        ///  <example>
        ///    <code>
        ///      myControl.CallCommand("upTime", 100);
        ///      ...
        ///      ...
        ///    </code>
        ///    Translates into:
        ///    <code>
        ///      NetJsWire.App.myControl.upTime(100);
        ///    </code>
        ///    in client code.
        ///  </example>
        ///  <remarks>
        ///    This is a Dictionary so if you call this method several times the value gets replaced and the last one is executed.
        ///    This can be usefull for Events like ComboBox.SelectedIndexChanged that can occur several times before the combo gets 
        ///    rendered and this way we can be sure that only the last value set is passed to the client.
        ///  </remarks>
        ///</summary>
        ///<param name = "name">Command name.</param>
        ///<param name = "value">The value to pass to the command function.</param>
        public virtual void CallCommand( string name, object value )
        {
            if ( Properties.ContainsKey( name ) )
            {
                Properties[ name ] = value;
            }
            else
            {
                Properties.Add( name, value );
            }
            WebApplication.Instance.HasChanges = true;
        }

        ///<summary>
        ///  Executes a client command.
        ///  <example>
        ///    <code>
        ///      myControl.AddCommand("upTime", 100);
        ///      ...
        ///      ...
        ///    </code>
        ///    Translates into:
        ///    <code>
        ///      NetJsWire.App.myControl.upTime(100);
        ///    </code>
        ///    in client code.
        ///  </example>
        ///  <remarks>
        ///    This is a FIFO Queue, you can call this method several times and the commands are going to be executed one after the other.
        ///  </remarks>
        ///</summary>
        ///<param name = "name">Command name.</param>
        ///<param name = "value">The value(s) to pass to the command function.</param>
        public virtual void AddCommand( string name, params object[] value )
        {
            Commands.Enqueue( new Command( name, value ) );
            WebApplication.Instance.HasChanges = true;
        }

        /// <summary>
        ///   Adds a direct command to the queue, this function is for passing arbitrary code to the client it works like this:
        ///   <example>
        ///     <code>
        ///       MyWidgetRenderer.AddDirectCommand("x = ", "SomeFunctionOrVar()");
        ///     </code>
        ///     Gest translated into:
        ///     <code>
        ///       x = SomeFunctionOrVar();
        ///     </code>
        ///   </example>
        ///   <remarks>
        ///     This method adds a semi colon (;) to the end of the statement, you dont need to add one.
        ///   </remarks>
        /// </summary>
        /// <param name = "command"></param>
        /// <param name = "value"></param>
        public virtual void AddDirectCommand( string command, object value )
        {
            DirectCommands.Enqueue( new Command( command, value ) );
            WebApplication.Instance.HasChanges = true;
        }

        ///<summary>
        ///  Adds or changes a style property, this affects the style of the DOM object in client.
        ///</summary>
        ///<param name = "name">Name of the style property.</param>
        ///<param name = "value">Value of the style property.</param>
        public virtual void AddStyleProperty( string name, object value )
        {
            SetStyleProperty( name, value );
        }

        ///<summary>
        ///  Sets a client property on the object.
        ///  <example>
        ///    <code>
        ///      myControl.SetEqualProperty("upTime", 100);
        ///    </code>
        ///    Translates into:
        ///    <code>
        ///      NetJsWire.App.myControl.upTime = 100;
        ///    </code>
        ///    in client code.
        ///  </example>
        ///</summary>
        ///<param name = "name">BlockRefresh name.</param>
        ///<param name = "value">The value to set the property to.</param>
        public virtual void SetEqualProperty( string name, object value )
        {
            if ( EqualProperties.ContainsKey( name ) )
            {
                EqualProperties[ name ] = value;
            }
            else
            {
                EqualProperties.Add( name, value );
            }
            WebApplication.Instance.HasChanges = true;
        }

        public virtual void AddEvent( string name, Delegate value )
        {
            //throw new NotImplementedException( "The method or operation is not implemented." );
        }

        /// <summary>
        ///   Adds a command to be executed in Javascript, it can be a call to a function or a method.
        ///   <remarks>
        ///     This method prepends the Widget name its for calling functios or constructing objects.
        ///   </remarks>
        /// </summary>
        /// <param name = "command">The name of the method or function to execute.</param>
        /// <param name = "value">The parameter to pass to the method.</param>
        public virtual void AddApplicationTailCommand( string command, object value )
        {
            var nodeCommand = WebApplication.Instance.XmlDocument[ "class" ][ "code" ].AppendChild( WebApplication.Instance.XmlDocument.CreateNode( "element", "codeline", "" ) );
            nodeCommand.InnerText = string.Format( "{0}.{1}({2});", ( IsInContainer ? GetCmp : ClientID ), command, ( ( value != null ) ? ( value is bool ? value.ToString().ToLower() : value.ToString() ) : "" ) );
            WebApplication.Instance.HasChanges = true;
        }

        /// <summary>
        ///   An equal command to be executed in Javascript, this is usefull if you need to set the value of a variable that doesnt belong to a Widget.
        ///   <remarks>
        ///     This method does not prepends the Widget name its for calling functios or constructing objects.
        ///   </remarks>
        ///   <example>
        ///     <code>
        ///       MyWidget.Renderer.AddApplicationTailDirectEqualCommand("MyJavascriptObjectOrVar", true);
        ///     </code>
        ///     Gets translated into:
        ///     <code>
        ///       MyJavascriptObjectOrVar = true;
        ///     </code>
        ///   </example>
        /// </summary>
        /// <param name = "objOrVar">The name of your Javascript object or variable.</param>
        /// <param name = "value">The new value of the Javascript object or variable.</param>
        public virtual void AddApplicationTailDirectEqualCommand( string objOrVar, object value )
        {
            var nodeCommand = WebApplication.Instance.XmlDocument[ "class" ][ "code" ].AppendChild( WebApplication.Instance.XmlDocument.CreateNode( "element", "codeline", "" ) );
            nodeCommand.InnerText = string.Format( "{0}={1};", objOrVar, ( ( value != null ) ? value.ToString() : "" ) );
            WebApplication.Instance.HasChanges = true;
        }

        ///<summary>
        ///  This function adds code to be executed arbitrarily in Javascript, prepending the Widget client name (ClientID).
        ///  <example>
        ///    This code in server:
        ///    <code>
        ///      MyWidget.Renderer.AddRawDotCode( "CallMyFunction()" );
        ///    </code>
        ///    Gets translated into this Javascript code:
        ///    <code>
        ///      MyWidgetClientId.CallMyFunction();
        ///    </code>
        ///    <remarks>
        ///      This method appends a semicolon to the end of the statement (;).
        ///    </remarks>
        ///  </example>
        ///</summary>
        ///<param name = "code">The code to be executed</param>
        public virtual void AddRawDotCode( string code )
        {
            var nodeCommand = WebApplication.Instance.XmlDocument[ "class" ][ "code" ].AppendChild( WebApplication.Instance.XmlDocument.CreateNode( "element", "codeline", "" ) );
            nodeCommand.InnerText = string.Format( "{0}.{1};", ( IsInContainer ? GetCmp : ClientID ), code );
            WebApplication.Instance.HasChanges = true;
        }

        /// <summary>
        ///   Adds code that needs to be executed before this Widget is created, it can be any valid Javascript code.
        /// </summary>
        /// <param name = "precode">The code to be executed.</param>
        public void AddPrecode( string precode )
        {
            XmlNode preCodeNode = GetTopLevelContainer( this ).ControlNode[ "precode" ];

            preCodeNode.AppendChild( WebApplication.Instance.XmlDocument.CreateNode( "element", "codeline", "" ) ).InnerText = precode;
        }

        /// <summary>
        ///   Adds code that needs to be executed before this Widget is created, it can be any valid Javascript code.
        /// </summary>
        /// <param name = "command">The command to be executed.</param>
        /// <param name = "code">The code to be executed.</param>
        public void AddPrecodeCommand( string command, string code )
        {
            //XmlNode preCodeNode = GetTopLevelContainer( this ).ControlNode[ "precode" ];
            PreCodeCommands.Enqueue( new Command( command, code ) );
            //preCodeNode.AppendChild( WebApplication.Instance.XmlDocument.CreateNode( "element", "codeline", "" ) ).InnerText = string.Format( "{0}.{1}({2});", GetCmp, command, code.Substring( 0, val.Length - 1 ) );
        }

        /// <summary>
        ///   Returns the parent renderer name (or part, body in case of panels) this widget is going to be rendered into. (Deprecated)
        /// </summary>
        [Browsable( false )]
        public string ClientParentToRender
        {
            get
            {
                if ( string.IsNullOrEmpty( _clientParentToRender ) )
                {
                    if ( Base is MenuItem )
                    {
                        //TODO: all this boxing unboxing... it has to be a better way.
                        // there is a better way, have to check timing with full reflection...
                        //if ( ( ( Base as Ui.ToolBar.MenuItem ).OwnerItem is Ui.ToolBar.DropDownButton ) )
                        //_clientParentToRender = ( ( ( Base as Ui.ToolBar.MenuItem ).OwnerItem as Ui.ToolBar.DropDownButton ).Renderer as Element ).ClientID;
                        _clientParentToRender = Base.OwnerItem.Renderer.ClientID;
                        //else
                        //_clientParentToRender = Base.OwnerItem.Renderer.ClientID;
                        //_clientParentToRender = ( ( Base as Ui.ToolBar.MenuItem ).OwnerItem as IWidget ).Renderer.ClientID;

                        _clientParentToRender += "_Menu";
                    }
                    else if ( Base is MenuSeparator )
                    {
                        _clientParentToRender = "";
                        //_clientParentToRender = ( ( Base as Ui.ToolBar.MenuSeparator ).OwnerItem as Ui.ToolBar.DropDownButton ).Renderer.ClientID;
                        //_clientParentToRender += "_Menu";
                    }
                    else if ( Base is Button )
                    {
                        //_clientParentToRender = ( ( ( Base as Ui.ToolBar.Button ).Owner as Ui.ToolBar.ToolBar ).Renderer as Element ).ClientID;
                        _clientParentToRender = Base.Owner.Renderer.ClientID;
                    }
                    else if ( Base is DropDownButton )
                    {
                        //_clientParentToRender = ( ( ( Base as Ui.ToolBar.DropDownButton ).Owner as Ui.ToolBar.ToolBar ).Renderer as Element ).ClientID;
                        _clientParentToRender = Base.Owner.Renderer.ClientID;
                    }
                    else if ( Base is TextField )
                    {
                        //_clientParentToRender = ( ( ( Base as Ui.ToolBar.TextField ).Owner as Ui.ToolBar.ToolBar ).Renderer as Element ).ClientID;
                        _clientParentToRender = Base.Owner.Renderer.ClientID;
                    }
                    else if ( Base is ColorMenu )
                    {
                        if ( Base.OwnerItem is DropDownButton )
                            _clientParentToRender = Base.OwnerItem.Renderer.ClientID;
                        else
                            _clientParentToRender = Base.OwnerItem.Renderer.ClientID;

                        _clientParentToRender += "_Menu";
                    }
                    else if ( BaseParent is Window )
                    {
                        _clientParentToRender = ParentRenderer.ClientID;
                        _clientParentToRender += ".body";
                    }
                    else if ( BaseParent is FieldSet )
                    {
                        _clientParentToRender = ParentRenderer.ClientID;
                        _clientParentToRender += ".body";
                    }
                    else if ( BaseParent is ClientDocument )
                    {
                        _clientParentToRender = "document.body";
                    }
                    else if ( BaseParent is TabPage )
                    {
                        //Control ctrl = ( ( Control ) Base ).Parent.Parent;
                        //if ( !( ( Element ) ( ( IWidget ) ctrl ).Renderer ).Rendered )
                        //{
                        //    ( ( Element ) ( ( IWidget ) ctrl ).Renderer ).Render();
                        //}
                        //_clientParentToRender = ( ( Element ) ( ( IWidget ) ctrl ).Renderer ).ClientID;
                        //_clientParentToRender += "." + ( BaseParent as TabPage ).Name + ".body";
                        _clientParentToRender = string.Empty;
                    }
                    else if ( BaseParent is Panel )
                    {
                        if ( Base is BorderLayout )
                        {
                            _clientParentToRender = ParentRenderer.ClientID;
                        }
                        else
                        {
                            _clientParentToRender = ParentRenderer.ClientID;
                            _clientParentToRender += ".body";
                        }
                    }
                    else if ( BaseParent is BorderLayout )
                    {
                        _clientParentToRender = ParentRenderer.ClientID;
                    }
                    else if ( BaseParent is FormPanel )
                    {
                        _clientParentToRender = ParentRenderer.ClientID;
                    }
                    else if ( BaseParent == null )
                    {
                        _clientParentToRender = "document.body";
                    }
                    else
                    {
                        _clientParentToRender = ParentRenderer.InternalID;
                    }
                }

                return _clientParentToRender;
            }
        }

        /// <summary>
        ///   Returns the parent of our base Widget.
        /// </summary>
        [Browsable( false )]
        public Component BaseParent
        {
            get
            {
                if ( Base is Control )
                    return ( ( Control ) Base ).Parent;

                if ( Base is Component && Base is IDataStore )
                {
                    return Base.ContainerControl;
                }

                if ( Base is Component && Base is ToolStripItem )
                {
                    return Base.OwnerItem ?? Base.Owner;
                }

                throw new Exception( "The parent element is not convertible to a known Type." );
            }
        }

        /// <summary>
        ///   The ["control"] node that represents this Widget in XML before its procesed.
        /// </summary>
        [Browsable( false )]
        public XmlNode ControlNode
        {
            get
            {
                // Try to get a reference to the node
                //_controlNode = WebApplication.Instance.XmlDocument.DocumentElement.SelectSingleNode( "//class//controls//control[@id='" + InternalID + "']" );
                _controlNode = WebApplication.Instance.XmlDocument.DocumentElement.SelectSingleNode( "descendant::control[@id='" + InternalID + "']" );

                if ( _controlNode == null )
                {
                    _controlNode =
                        WebApplication.Instance.XmlDocument.DocumentElement[ "controls" ].AppendChild(
                            WebApplication.Instance.XmlDocument.CreateNode( XmlNodeType.Element, "control", "" ) );
                    _controlNode.Attributes.Append( WebApplication.Instance.XmlDocument.CreateAttribute( "id" ) ).Value
                        = InternalID;
                    _controlNode.Attributes.Append( WebApplication.Instance.XmlDocument.CreateAttribute( "name" ) ).
                        Value = ClientID;
                    SetAttribute( _controlNode, "type", ClassName );
                    _controlNode.AppendChild( WebApplication.Instance.XmlDocument.CreateNode( XmlNodeType.Element,
                                                                                              "propertys", "" ) );
                    _controlNode.AppendChild( WebApplication.Instance.XmlDocument.CreateNode( XmlNodeType.Element,
                                                                                              "equalpropertys", "" ) );
                    _controlNode.AppendChild( WebApplication.Instance.XmlDocument.CreateNode( XmlNodeType.Element,
                                                                                              "events", "" ) );
                    if ( Base is Panel )
                    {
                        _controlNode.AppendChild( WebApplication.Instance.XmlDocument.CreateNode( XmlNodeType.Element,
                                                                                                  "items", "" ) );
                    }
                    _controlNode.AppendChild( WebApplication.Instance.XmlDocument.CreateNode( XmlNodeType.Element,
                                                                                              "precode", "" ) );
                    _controlNode.AppendChild( WebApplication.Instance.XmlDocument.CreateNode( XmlNodeType.Element,
                                                                                              "code", "" ) );
                    _controlNode.AppendChild( WebApplication.Instance.XmlDocument.CreateNode( XmlNodeType.Element,
                                                                                              "options", "" ) );
                    _controlNode.AppendChild( WebApplication.Instance.XmlDocument.CreateNode( XmlNodeType.Element,
                                                                                              "styles", "" ) );
                    _controlNode.AppendChild( WebApplication.Instance.XmlDocument.CreateNode( XmlNodeType.Element,
                                                                                              "controls", "" ) );
                    _controlNode.AppendChild( WebApplication.Instance.XmlDocument.CreateNode( XmlNodeType.Element,
                                                                                              "buttons", "" ) );
                    if ( Base is ComboBox )
                        _controlNode.AppendChild( WebApplication.Instance.XmlDocument.CreateNode( XmlNodeType.Element,
                                                                                                  "data", "" ) );
                    if ( Rendered )
                        _controlNode.Attributes.Append( WebApplication.Instance.XmlDocument.CreateAttribute( "render" ) )
                            .Value = "false";

                    if ( Base is IWidgetContainer )
                    {
                        _controlNode.AppendChild( WebApplication.Instance.XmlDocument.CreateNode( XmlNodeType.Element,
                                                                                                  "layoutConfig", "" ) );
                    }
                }

                return _controlNode;
            }
            set { _controlNode = value; }
        }

        /// <summary>
        ///   True if this IRenderer its a <see cref = "ITopLevelContainer" />.
        ///   <remarks>
        ///     There are only 2 types of ITopLevelContainer so far: <see cref = "NetJsWire.Widgets.Ui.Window.Window">Window</see> and <see cref = "NetJsWire.Widgets.Core.ClientDocument">ClientDocument</see>.
        ///   </remarks>
        /// </summary>
        [Browsable( false )]
        public bool TopLevelContainer
        {
            get { return Base is ITopLevelContainer; }
        }

        [Browsable( false )]
        [JsonConverter( typeof ( NotQuotedJsonStringConverter ) )]
        public List<object> Plugins { get; set; }

        /// <summary>
        ///   Adds a client listener to the Widget for the event.
        /// </summary>
        /// <param name = "name">The name of the event to listen for Ex. 'click'.</param>
        /// <param name = "function">The function to be executed, can be the name of an existent function ar an anonymous function declared inline.</param>
        public void AddClientEvent( string name, string function )
        {
            ClientEvents.Add( name, function );
            WebApplication.Instance.HasChanges = true;
        }

        public void AddKeyNav( string keys, string function )
        {
            KeyNavs.Add( keys, function );
            WebApplication.Instance.HasChanges = true;
        }

        public void SetLayoutConfigProperty( string propName, object value )
        {
            var attr = ControlNode[ "layoutConfig" ].Attributes[ propName ] ??
                                ControlNode[ "layoutConfig" ].Attributes.Append(
                                    WebApplication.Instance.XmlDocument.CreateAttribute( propName ) );
            attr.Value = value is bool ? value.ToString().ToLower() : value.ToString();
        }

        public void RemoveLayoutConfigProperty( string propName )
        {
            var attr = ControlNode[ "layoutConfig" ].Attributes[ propName ];

            if ( attr != null )
            {
                ControlNode[ "layoutConfig" ].Attributes.Remove( attr );
            }
        }

        public void SetAttribute( string name, object value )
        {
            var attr = ControlNode.Attributes[ name ] ??
                                ControlNode.Attributes.Append( WebApplication.Instance.XmlDocument.CreateAttribute( name ) );
            attr.Value = value is bool ? value.ToString().ToLower() : value.ToString();
        }

        /// <summary>
        ///   True if the events are already configured for this Renderer
        /// </summary>
        public bool EventsAdded { get; set; }

        /// <summary>
        ///   Returns a safe call to Ext.getCmp to return our component, useful if you dont have a client reference.
        /// </summary>
        [Browsable( false )]
        public string GetCmp
        {
            get { return "Ext.getCmp('" + InternalID + "')"; }
        }

        #endregion

        private static void GetClientIDWithNamespace( ref string clientid, dynamic o )
        {
            PropertyInfo piParent = o.GetType().GetProperty( "Parent" );
            if ( piParent != null )
            {
                //dynamic parent = piParent.GetValue( o, null );
                var parent = o.Parent;
                if ( parent != null )
                {
                    clientid = string.IsNullOrEmpty( clientid ) ? parent.Name : clientid.Insert( 0, parent.Name + "." );
                    //clientid = clientid.Insert( 0, parent.GetType().GetProperty( "Name" ).GetValue( parent, null ). ToString() + "." );
                    GetClientIDWithNamespace( ref clientid, parent );
                }
            }
            else
            {
                dynamic parent;
                //For stores
                if ( o is Store )
                    parent = o.ContainerControl;
                    // For ToolStripItems
                else if ( o is MenuItem || o is MenuSeparator )
                    parent = o.OwnerItem;
                    //piParent = o.GetType().GetProperty( "OwnerItem" );
                else
                    parent = o.Owner;
                //piParent = o.GetType().GetProperty( "Owner" );
                if ( parent != null )
                {
                    //object parent = piParent.GetValue( o, null );
                    string parentName = parent != null
                                            ? parent.Name
                                            : null;
                    if ( parent != null && !string.IsNullOrEmpty( parentName ) )
                    {
                        clientid = string.IsNullOrEmpty( clientid ) ? parentName : clientid.Insert( 0, parentName + "." );
                        GetClientIDWithNamespace( ref clientid, parent );
                    }
                }
            }
        }

        /// <summary>
        ///   Gets our own hash.
        /// </summary>
        /// <returns></returns>
        public int? ToHashCode()
        {
            return _hashCode;
        }

        protected virtual XmlNode SetStyleProperty( string name, object value )
        {
            var attr = WebApplication.Instance.XmlDocument.CreateAttribute( name );
            var valToPrint = value.ToString();
            if ( ( name == "top" || name == "left" || name == "width" || name == "height" || name == "right" ||
                   name == "bottom" ) && !( value.ToString().Contains( "%" ) ) )
            {
                attr.Value = Utils.SingleQuotePx( valToPrint );
            }
            else if ( name == "position" )
                attr.Value = Utils.SingleQuote( valToPrint );
            else
                attr.Value = valToPrint;
            StylesNode.Attributes.Append( attr );
            WebApplication.Instance.HasChanges = true;
            return StylesNode;
        }

        /// <summary>
        ///   Adds a command to be executed in Javascript, it can be a call to a function or a method.
        ///   <remarks>
        ///     This method does not prepends the Widget name its for calling functios or constructing objects.
        ///   </remarks>
        /// </summary>
        /// <param name = "command">The name of the method or function to execute.</param>
        /// <param name = "value">The parameter to pass to the method.</param>
        public virtual void AddApplicationTailSimpleCommand( string command, object value )
        {
            var nodeCommand = WebApplication.Instance.XmlDocument[ "class" ][ "code" ].AppendChild( WebApplication.Instance.XmlDocument.CreateNode( "element", "codeline", "" ) );
            nodeCommand.InnerText = string.Format( "{0}({1});", command, ( ( value != null ) ? ( value is bool ? value.ToString().ToLower() : value.ToString() ) : "" ) );
            WebApplication.Instance.HasChanges = true;
        }

        internal void MoveControlNodeBeforeTopLevelContainer()
        {
            var topLevelContainer = GetTopLevelContainer( this );

            var oldNode = ControlNode.CloneNode( true );
            ControlNode.ParentNode.RemoveChild( ControlNode );
            WebApplication.Instance.XmlDocument[ "class" ][ "controls" ].InsertBefore( oldNode, topLevelContainer.ControlNode );
        }

        protected IRenderer GetTopLevelContainer( IRenderer widget )
        {
            if ( widget.TopLevelContainer )
            {
                return widget;
            }

            if ( widget.BaseParent != null )
                return GetTopLevelContainer( ( ( IWidget ) widget.BaseParent ).Renderer );

            return widget;
        }

        public bool IsChildOfType( Type type )
        {
            return IsChildOfType( this, type );
        }

        private static T GetParentRenderer<T>( IWidget widget ) where T : IWidget
        {
            if ( widget is T )
            {
                return ( T ) widget;
            }

            if ( widget.Renderer.BaseParent != null )
                return GetParentRenderer<T>( widget.Renderer.BaseParent as IWidget );

            return default( T );
        }

        public T GetParentRenderer<T>() where T : IWidget
        {
            return GetParentRenderer<T>( Base as IWidget );
        }

        ///<summary>
        ///</summary>
        ///<param name = "widget"></param>
        ///<param name = "type"></param>
        ///<returns></returns>
        private static bool IsChildOfType( IRenderer widget, Type type )
        {
            if ( widget.BaseParent != null )
            {
                if ( widget.BaseParent.GetType() == type )
                {
                    return true;
                }

                if ( widget.BaseParent is IWidget )
                {
                    return IsChildOfType( ( widget.BaseParent as IWidget ).Renderer, type );
                }

                return false;
            }

            return false;
        }

        protected static void SetAttribute( XmlNode node, string name, object value )
        {
            var attr = node.Attributes[ name ] ??
                       node.Attributes.Append( WebApplication.Instance.XmlDocument.CreateAttribute( name ) );
            attr.Value = value != null ? ( value is bool ? value.ToString().ToLower() : value.ToString() ) : "null";
        }

        internal void OnAfterRender()
        {
            object o = AfterRender;
            if ( o != null )
                AfterRender( this, EventArgs.Empty );
        }

        internal void EnsureParentNodePosition()
        {
            if ( BaseParent is IWidget && !( BaseParent is IWidgetContainer ) )
            {
                if ( !( BaseParent as IWidget ).Renderer.Rendered )
                {
                    ( BaseParent as IWidget ).Renderer.Render();
                }

                var parentNode = ControlNode.CloneNode( true );
                ControlNode.ParentNode.RemoveChild( ControlNode );
                ( BaseParent as IWidget ).Renderer.ControlNode[ "controls" ].AppendChild( parentNode );
            }
            else if ( BaseParent is IWidgetContainer )
            {
                if ( !( ( IWidget ) BaseParent ).Renderer.Rendered )
                {
                    ( BaseParent as IWidget ).Renderer.Render();
                }


                var parentNode = ControlNode.CloneNode( true );
                ControlNode.ParentNode.RemoveChild( ControlNode );

                if ( Base is IDataStore )
                {
                    // Stores need to get rendered before any other container, so we put it before the TopLevelContainer of this tree.
                    WebApplication.Instance.XmlDocument[ "class" ][ "controls" ].InsertBefore( parentNode, GetTopLevelContainer( this ).ControlNode );
                }
                else
                {
                    if ( Base is Ui.Form.Button && Base.AutoPositioned )
                    {
                        ( BaseParent as IWidget ).Renderer.ControlNode[ "buttons" ].AppendChild( parentNode );
                    }
                    else
                    {
                        ( BaseParent as IWidget ).Renderer.ControlNode[ "controls" ].AppendChild( parentNode );
                    }
                }
            }
        }

        private void EnsureParentNamespaceCreated()
        {
            for ( var i = 0; i < ControlNode[ "precode" ].ChildNodes.Count; i++ )
            {
                if ( ControlNode[ "precode" ].ChildNodes[ i ].InnerText ==
                     ( ( IWidget ) ( BaseParent ) ).Renderer.ClientID )
                {
                    break;
                }
            }

            var nodeNs =
                ControlNode[ "precode" ].AppendChild( WebApplication.Instance.XmlDocument.CreateNode( "element",
                                                                                                      "precodeline", "" ) );
            nodeNs.InnerText = "var " + ( ( IWidget ) ( BaseParent ) ).Renderer.ClientID + " = {};";
        }

        /// <summary>
        ///   Adds an arbitrary command to be executed in Javascript after the Widget instantiation.
        ///   <example>
        ///     <code>
        ///       MyWidget.Renderer.AddApplicationTailDirectCommand( "MyJavascriptFunction", "{ val1:1, val2:'2' }" );
        ///     </code>
        ///     Gets translated into:
        ///     <code>
        ///       MyWidgetClientId.MyJavascriptFunction( { val1:1, val2:'2' } );
        ///     </code>
        ///   </example>
        /// </summary>
        /// <param name = "command">The command name.</param>
        /// <param name = "value">The value to pass to the command function.</param>
        public void AddApplicationTailDirectCommand( string command, object value )
        {
            var nodeCommand = WebApplication.Instance.XmlDocument[ "class" ][ "code" ].AppendChild( WebApplication.Instance.XmlDocument.CreateNode( "element", "codeline", "" ) );
            nodeCommand.InnerText = string.Format( "{0}({1});", command, ( ( value != null ) ? value.ToString() : "" ) );
            WebApplication.Instance.HasChanges = true;
        }
    }
}